package com.aofeng.hybrid.sync;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.AsyncTask;
import com.aofeng.hybrid.orm.AssociationMeta;
import com.aofeng.hybrid.orm.DBMeta;
import com.aofeng.hybrid.orm.EntityMeta;
import com.aofeng.hybrid.service.LogicMapper;
import com.aofeng.hybrid.service.SqlMapper;
import com.aofeng.hybrid.util.CommUtil;
import com.aofeng.hybrid.util.LogUtil;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

/**
 * 同步离线网站文件和数据库
 * @author LGY
 *
 */
public class SyncDBAndPagesTask extends AsyncTask<SyncParam, Integer, Boolean>{
	private IProgressNotifier progressNotifier;
	private Date newModifiedDate;
    private Context context;
    public static String UPDATE_MOST_RECENT_TIME = "UPDATE_MOST_RECENT_TIME";

    /**
	 * 
	 * @param pn  进度变化时调用
	 */
	public SyncDBAndPagesTask(Context context, IProgressNotifier pn)
	{
        this.context = context;
		progressNotifier = pn;
	}
	
	/**
	 * 异步同步工作
	 */
	@Override
	protected Boolean doInBackground(SyncParam... params) {
		try
		{
			SyncParam sp = params[0];

			//检查是否需要同步    version url, last modified date
			if(!needsSync(sp.getUpdateUrl(), sp.getLastUpdateTime()))
				return false;

			//      url, tables
			alignDB(sp.getDbMetaUrl(), sp.getEntities());

			//提取页面url，远程目录, 本地目录, 下载服务url
			alignH5Pages(sp.getDirUrl() + URLEncoder.encode(sp.getRemoteWWWRoot()).replace("+", "%20"), sp.getRemoteWWWRoot(), sp.getLocalPathRoot() + "www/", sp.getFileDownloadUrl());

            //提取sql和业务配置
            alignSqlAndBz(sp.getDirUrl() + URLEncoder.encode(sp.getRemoteBzRoot()).replace("+", "%20"), sp.getRemoteBzRoot(), sp.getLocalPathRoot(), sp.getFileDownloadUrl());

            //提取业务和sql
            SqlMapper.loadMap(context);
            LogicMapper.loadMap(context);

			//同步成功
			if(newModifiedDate == null)
				newModifiedDate = new Date(70,1,1);

			CommUtil.savePreference(context, sp.getAppId(), UPDATE_MOST_RECENT_TIME, (newModifiedDate.getTime()+""));
			return Boolean.valueOf(true);
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return Boolean.valueOf(false);
		}
	}

    private void alignSqlAndBz(String dirUrl, String remoteBzRoot, String localPathRoot, String fileDownloadUrl) throws Exception {
        HttpGet getMethod = new HttpGet(dirUrl);
        HttpClient httpClient = CommUtil.getTimeoutHttpClient();
        HttpResponse res = httpClient.execute(getMethod);
        int code = res.getStatusLine().getStatusCode();
        if (code == 200){
            SyncFileHelper.makeRootDir(localPathRoot + "sqls/");
            SyncFileHelper.makeRootDir(localPathRoot + "logics/");
            String result = EntityUtils.toString(res.getEntity(), "UTF-8");
            String[] files = result.split("\\|", -1);
            int i = 0;
            int n = files.length;
            for(String file : files)
            {
                LogUtil.d("同步业务文件", "同步文件:" + file);
                publishProgress(60 + (i*40)/n);
                i++;
                if(file.contains("sql.xml,") || file.contains("logic.xml,")|| file.contains("\\sqls\\") || file.contains("\\logics\\")  )
                    updateFile(file, remoteBzRoot, localPathRoot, fileDownloadUrl);
            }
            publishProgress(100);
        }
        else
            throw new Exception("同步文件出错。");
    }

    @SuppressLint("SimpleDateFormat")
	private boolean needsSync(String checkVersionUrl, String lastModifiedTime) throws Exception {
		if(lastModifiedTime == null)
			return true;
		HttpGet getMethod = new HttpGet(checkVersionUrl);
		HttpClient httpClient = CommUtil.getTimeoutHttpClient();
		HttpResponse res = httpClient.execute(getMethod);
		int code = res.getStatusLine().getStatusCode();
		if (code == 200){
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			JSONObject jo = new JSONObject(EntityUtils.toString(res.getEntity(), "UTF-8"));
			LogUtil.d("Async server timestamp", jo.getString(("value")));
			LogUtil.d("Async client timestamp", sdf.format(new Date(Long.parseLong(lastModifiedTime))));
			newModifiedDate = sdf.parse(jo.getString("value"));
			if(new Date(Long.parseLong(lastModifiedTime)).before(newModifiedDate))
				return true;
			else
				return false;
		}
		return false;
	}

	/**
	 * 同步页面
	 * @param fetchUrl
	 * @throws Exception 
	 */
	private void alignH5Pages(String fetchUrl, String remoteDir, String localDirPrefix, String fileDownloadUrl) throws Exception {
		HttpGet getMethod = new HttpGet(fetchUrl);
		HttpClient httpClient = CommUtil.getTimeoutHttpClient();
		HttpResponse res = httpClient.execute(getMethod);
		int code = res.getStatusLine().getStatusCode();
		if (code == 200){
			SyncFileHelper.makeRootDir(localDirPrefix);
			String result = EntityUtils.toString(res.getEntity(), "UTF-8");
			String[] files = result.split("\\|", -1);
			int i = 0;
			int n = files.length;
			for(String file : files)
			{
				LogUtil.d("同步文件", "同步文件:" + file);
				updateFile(file, remoteDir, localDirPrefix,fileDownloadUrl);
				publishProgress(30 + (i*30)/n);
				i++;
			}
			publishProgress(60);
		}
		else
			throw new Exception("同步文件出错。");
		
	}

	/**
	 * 更新单个文件
	 * @param fileNameAndDate
	 * @throws Exception 
	 */
	private void updateFile(String fileNameAndDate, String remoteDir, String localDirPrefix, String fileDownloadUrl) throws Exception {
		String[] pair = fileNameAndDate.split(",");
		//去掉无关目录
		String path = pair[0].replace(remoteDir, "");
		long timestamp = Long.parseLong(pair[1]);
		SyncFileHelper.processFile(localDirPrefix, path, timestamp, remoteDir, fileDownloadUrl);
	}

	/**
	 * 生成或更新表
	 * @param jo
	 * @throws Exception 
	 */
	private void createOrUpdateTable(JSONObject jo) throws Exception {
        String tableName = jo.getString("__table__");
		if(!SyncDBHelper.hasTable(context, tableName))
			SyncDBHelper.createTable(context, tableName, jo);
		else
			SyncDBHelper.updateTable(context, tableName, jo);
	}

	/**
	 * 同步数据库
	 * @param metaUrl
	 */
	private void alignDB(String metaUrl, String tables) throws Exception {
		HttpPost postMethod = new HttpPost(metaUrl);
		HttpClient httpClient = CommUtil.getTimeoutHttpClient();
		postMethod.setEntity(new StringEntity(tables,"UTF-8"));
		HttpResponse res = httpClient.execute(postMethod);
		int code = res.getStatusLine().getStatusCode();
		if (code == 200){
			SyncDBHelper.createDBIfNotExist(context);
			JSONObject jo = new JSONObject(EntityUtils.toString(res.getEntity(), "UTF-8"));
			@SuppressWarnings("unchecked")
			Iterator<String> iterator = jo.keys();
			int n = jo.length();
			int i = 0;
            DBMeta meta = DBMeta.getMeta(context);
            meta.clearMeta();
            while (iterator.hasNext()){
				String entityName = iterator.next();
				LogUtil.d("同步表：", "处理表:" + entityName);
                JSONObject jsonTable = jo.getJSONObject(entityName);
				createOrUpdateTable(jsonTable);
				updateDBMetadata(meta,entityName,jsonTable);
				publishProgress((i*20)/n);
				i++;
			}
            publishProgress(20);
			iterator = jo.keys();
            i = 0;
            while(iterator.hasNext()) {
                String entityName = iterator.next();
                LogUtil.d("同步表：", "处理表:" + entityName);
                JSONObject jsonTable = jo.getJSONObject(entityName);
                createOrUpdateLinkColumn(jsonTable);
                publishProgress(20 + (i*10)/n);
            }
			publishProgress(30);
            meta.saveDBMeta(context);
		}
		else
			throw new Exception("同步数据库出错。");
	}

    /**
     * 生成关联的列字段
     * @param jsonTable
     */
    private void createOrUpdateLinkColumn(JSONObject jsonTable) throws Exception{
        JSONArray associations = jsonTable.getJSONArray("__associations__");
        for(int i=0; i< associations.length(); i++) {
            JSONObject pair = associations.getJSONObject(i);
            SyncDBHelper.createOrUpdateLinkColumn(context, pair.getString("table2"), pair.getString("key2"), pair.getString("type"));
        }
    }

    /**
     * 更新表元数据
     * @param metaJo
     */
    private void updateDBMetadata(DBMeta meta, String entityName, JSONObject metaJo) throws  Exception{
        //添加实体名、表名对应关系
        String tableName = metaJo.getString("__table__");
        meta.addTableNameMapping(entityName, tableName);

        //添加表的元数据
        EntityMeta em = new EntityMeta();
        em.entityName = entityName;
        JSONObject joKey = metaJo.getJSONObject("__primary__key__");
        em.id = joKey.getString("id");
        em.idType = joKey.getString("type");

        //添加属性映射
        JSONArray columns = metaJo.getJSONArray("__columns__");
        for(int i=0; i< columns.length(); i++) {
            JSONObject column = columns.getJSONObject(i);
            String attr = column.getString("attr");
            String field = column.getString("column");
            em.attrColMap.put(attr, field);
            em.colAttrMap.put(field, attr);
            em.attrType.put(attr, column.getString("type"));
        }

        List<AssociationMeta> links = new ArrayList<AssociationMeta>();

        //添加表的关联
        JSONArray associations = metaJo.getJSONArray("__associations__");
        for(int i=0; i< associations.length(); i++) {
            JSONObject pair = associations.getJSONObject(i);
            AssociationMeta am = new AssociationMeta();
            am.cascade = pair.getString("cascade");
            am.master_table = pair.getString("table");
            am.master_key = pair.getString("key");
            am.slave_table = pair.getString("table2");
            am.slave_key = pair.getString("key2");
            am.linkName = pair.getString("collection");
            am.type = pair.getString("type");
            links.add(am);
        }
        em.ams = links.toArray(new AssociationMeta[links.size()]);

        //添加实体元数据
        meta.addEntity(tableName, em);
    }

    @Override
	protected void onPreExecute() {
		super.onPreExecute();
		progressNotifier.prelude();
	}

	/**
	 * 同步完成
	 */
	@Override
	protected void onPostExecute(Boolean result) {
		super.onPostExecute(result);
		progressNotifier.notifyDone(result);
	}

	/**
	 * 通知进度
	 */
	@Override
	protected void onProgressUpdate(Integer... progress) {
		progressNotifier.notifyProgress(progress[0]);
	}
}
